﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using Microsoft.Win32;
using TeF.Runner.Common.Extensions;
using TeF.Runner.UI.Services;

namespace TeF.Runner.UI
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        #region Fields

        private readonly Window _window;
        private readonly IManager _manager;
        private readonly ITreeViewManager _treeViewManager;
        
        #endregion


        #region Constructors

        public MainWindowViewModel(Window window)
        {
            if (window == null)
                throw new ArgumentNullException("window");

            _window = window;
            _manager = ManagerFactory.Create();
            _manager.RunCompleted += ManagerRunCompleted;

            LoadTestAssemblies = new DelegateCommand(OnLoadTestAssembliesCanExecute, OnLoadTestAssembliesExecute);
            UnloadAllTestAssemblies = new DelegateCommand(OnUnloadAllTestAssembliesCanExecute, OnUnloadAllTestAssembliesExecute);
            UnloadSelectedTestAssemblies = new DelegateCommand(OnUnloadSelectedTestAssembliesCanExecute, OnUnloadSelectedTestAssembliesExecute);
            Exit = new DelegateCommand(OnExitCanExecute, OnExitExecute);
            RunSelectedTests = new DelegateCommand(OnRunSelectedTestsCanExecute, OnRunSelectedTestsExecute);
            RunFailedTests = new DelegateCommand(OnRunFailedTestsCanExecute, OnRunFailedTestsExecute);
            RunAllTests = new DelegateCommand(OnRunAllTestsCanExecute, OnRunAllTestsExecute);
            StopTests = new DelegateCommand(OnStopTestsCanExecute, OnStopTestsExecute);
            
            _treeViewManager = IoCHelper.Container.Get<ITreeViewManager>();
            _treeViewManager.SelectedItemsChanged += SelectedItemsChanged;
            SelectedItems = _treeViewManager.SelectedItems;

            Assemblies = new ObservableCollection<HierarchicalItemViewModel>();
        }
        
        #endregion


        #region Properties

        public ObservableCollection<HierarchicalItemViewModel> Assemblies { get; private set; }

        public IEnumerable<HierarchicalItemViewModel> SelectedItems
        {
            get
            {
                return _selectedItems;
            }
            private set
            {
                _selectedItems = value;
                FirePropertyChanged("SelectedItems");
                FireCanExecute();
            }
        }
        private IEnumerable<HierarchicalItemViewModel> _selectedItems;

        #endregion


        #region Commands

        public DelegateCommand LoadTestAssemblies { get; private set; }
        public DelegateCommand UnloadAllTestAssemblies { get; private set; }
        public DelegateCommand UnloadSelectedTestAssemblies { get; private set; }
        public DelegateCommand Exit { get; private set; }

        public DelegateCommand RunSelectedTests { get; private set; }
        public DelegateCommand RunFailedTests { get; private set; }
        public DelegateCommand RunAllTests { get; private set; }
        public DelegateCommand StopTests { get; private set; }

        #endregion


        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        private void FirePropertyChanged(string propertyName)
        {
            if (propertyName == null)
                throw new ArgumentNullException("propertyName");

            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion


        #region Methods
        

        #region Command Handlers
        
        private bool OnLoadTestAssembliesCanExecute(object parameter)
        {
            return true;
        }

        private void OnLoadTestAssembliesExecute(object parameter)
        {
            var dialog =
                new OpenFileDialog {
                    CheckFileExists = true,
                    AddExtension = true,
                    DefaultExt = ".dll",
                    Filter = ".NET libraries (.dll)|*.dll",
                    Multiselect = true
                };
            if (dialog.ShowDialog(_window) == true)
            {
                foreach (var fileName in dialog.FileNames)
                {
                    var testAssembly = _manager.LoadTestAssembly(fileName);
                    var assembly = new HierarchicalItemViewModel(testAssembly);
                    Assemblies.Add(assembly);
                }
            }
        }

        private bool OnUnloadAllTestAssembliesCanExecute(object parameter)
        {
            return Assemblies.Any();
        }

        private void OnUnloadAllTestAssembliesExecute(object parameter)
        {
            Assemblies.Clear();
        }

        private bool OnUnloadSelectedTestAssembliesCanExecute(object parameter)
        {
            return Assemblies.Any() && SelectedItems.Any();
        }

        private void OnUnloadSelectedTestAssembliesExecute(object parameter)
        {
            SelectedItems
                .Where(i => i.TestEntity is ITestAssembly)
                .DoForEach(i => Assemblies.Remove(i));
        }

        private bool OnExitCanExecute(object parameter)
        {
            return true;
        }

        private void OnExitExecute(object parameter)
        {
            _window.Close();
        }

        private bool OnRunSelectedTestsCanExecute(object parameter)
        {
            return Assemblies.Any() && SelectedItems.Any();
        }

        private void OnRunSelectedTestsExecute(object parameter)
        {
            var selectedTestEntities = SelectedItems.Select(i => i.TestEntity);

            _manager.RunAsync(selectedTestEntities);
        }

        private bool OnRunFailedTestsCanExecute(object parameter)
        {
            return false;
        }

        private void OnRunFailedTestsExecute(object parameter)
        {
            throw new NotImplementedException();
        }

        private bool OnRunAllTestsCanExecute(object parameter)
        {
            return Assemblies.Any();
        }

        private void OnRunAllTestsExecute(object parameter)
        {
            var assemblies = Assemblies.Select(i => i.TestEntity);

            _manager.RunAsync(assemblies);
        }

        private bool OnStopTestsCanExecute(object parameter)
        {
            return false;
        }

        private void OnStopTestsExecute(object parameter)
        {
            throw new NotImplementedException();
        }

        #endregion


        #region Event Handlers

        private void SelectedItemsChanged(object sender, EventArgs e)
        {
            SelectedItems = _treeViewManager.SelectedItems;
        }
        
        private void ManagerRunCompleted(object sender, EventArgs e)
        {
            FirePropertyChanged("SelectedItems");
        }
        
        #endregion


        #region Private

        private void FireCanExecute()
        {
            LoadTestAssemblies.FireCanExecuteChanged();
            UnloadAllTestAssemblies.FireCanExecuteChanged();
            UnloadSelectedTestAssemblies.FireCanExecuteChanged();
            Exit.FireCanExecuteChanged();
            RunSelectedTests.FireCanExecuteChanged();
            RunFailedTests.FireCanExecuteChanged();
            RunAllTests.FireCanExecuteChanged();
            StopTests.FireCanExecuteChanged();
        }

        #endregion


        #endregion
    }
}
